#include "bcast/broadcast.h"
#include "p2p/p2p.h"
#include "utils/utils.h"

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>


int main(int argc, char** argv)
{
	struct matrix_t* a;
	struct matrix_t* b;
	struct matrix_t* c;
	struct MPI_Info_t info;
	struct algorithm_data bcinfo;
	int i, j;

	MPI_Init(&argc, &argv);
	get_mpi_info(&info);

	if (argc == 1) {
		if (info.proc_id == 0) {
			fprintf(stderr, "No algorithm ID provided.\n");
			fprintf(stderr, "Usage: mat_prod <algorithm_id>\n");
			fprintf(stderr, "IDs:\n");
			fprintf(stderr, "\t0-> Broadcast\n");
			fprintf(stderr, "\t1-> Two-sided non-blocking\n");
			fprintf(stderr, "\t2-> Two-sided non-blocking non-pipelined\n");
			fprintf(stderr, "\t3-> Two-sided blocking\n");
			fprintf(stderr, "\t4-> One-sided active\n");
			fprintf(stderr, "\t5-> One-sided passive\n");
			fprintf(stderr, "\t6-> One-sided fence\n");
			fprintf(stderr, "\t7-> One-sided active non-pipelined\n");
			fprintf(stderr, "\t8-> One-sided passive non-pipelined\n");
			fprintf(stderr, "\t9-> One-sided fence non-pipelined\n");
			fprintf(stderr, "\t10-> No communication\n");
		}

		MPI_Barrier(MPI_COMM_WORLD);
		MPI_Finalize();
		return -1;
	}

	//read_matrix("A.txt", &a, &info, MAT_A);
	//read_matrix("B.txt", &b, &info, MAT_B);
	//alloc_matrix(&a, 4, 7);
	//alloc_matrix_b(&b, 7, 4);
	alloc_matrix(&a, 400/info.num_procs, 15000);
	alloc_matrix_b(&b, 15000, 8000/info.num_procs);
	alloc_matrix(&c, a->m, b->m*info.num_procs);
	for (i = 0; i < a->m; ++i) {
		for (j = 0; j < a->n; ++j) {
			set_elem(a, i, j, i);
			set_elem_b(b, j, i, j);
		}
	}

	bcinfo.a = a;
	bcinfo.b = b;
	bcinfo.c = c;
	bcinfo.info = &info;

	switch (atol(argv[1])) {
		case 0:
			broadcast(&bcinfo);
			break;
		case 1:
			p2p_twosided_nb(&bcinfo);
			break;
		case 2:
			p2p_twosided_nb_np(&bcinfo);
			break;
		case 3:
			p2p_twosided_b(&bcinfo);
			break;
		case 4:
			p2p_onesided(&bcinfo, ONESIDED_ACT);
			break;
		case 5:
			p2p_onesided(&bcinfo, ONESIDED_PAS);
			break;
		case 6:
			p2p_onesided(&bcinfo, ONESIDED_FEN);
			break;
		case 7:
			p2p_onesided_np(&bcinfo, ONESIDED_ACT);
			break;
		case 8:
			p2p_onesided_np(&bcinfo, ONESIDED_PAS);
			break;
		case 9:
			p2p_onesided_np(&bcinfo, ONESIDED_FEN);
			break;
		case 10:
			no_comm(&bcinfo);
			break;
		default:
			if (info.proc_id == 0) {
				fprintf(stderr, "No algorithm ID provided.\n");
				fprintf(stderr, "Usage: mat_prod <algorithm_id>\n");
				fprintf(stderr, "IDs:\n");
				fprintf(stderr, "\t0-> Broadcast\n");
				fprintf(stderr, "\t1-> Two-sided non-blocking\n");
				fprintf(stderr, "\t2-> Two-sided non-blocking non-pipelined\n");
				fprintf(stderr, "\t3-> Two-sided blocking\n");
				fprintf(stderr, "\t4-> One-sided active\n");
				fprintf(stderr, "\t5-> One-sided passive\n");
				fprintf(stderr, "\t6-> One-sided fence\n");
				fprintf(stderr, "\t7-> One-sided active non-pipelined\n");
				fprintf(stderr, "\t8-> One-sided passive non-pipelined\n");
				fprintf(stderr, "\t9-> One-sided fence non-pipelined\n");
				fprintf(stderr, "\t10-> No communication\n");
			}

			MPI_Barrier(MPI_COMM_WORLD);
			MPI_Finalize();
			return -1;
	}


	MPI_Barrier(MPI_COMM_WORLD);
	MPI_Finalize();
	return 0;
}
